﻿/********************************************************************************
** 作者： samfeng
** 创始时间：2012-7-15
** 描述：
** 功能：表达式解析类
*********************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;


namespace DBA.Common
{
    public class ExpressionHelper
    {
        public static Expression<Func<T, bool>> BuildPrefixMatchExpression<T>(string prefixParameterName, string path, char separator, bool separatorAtStart)
        {
            ParameterExpression param = null;
            return BuildPrefixMatchExpression<T>(prefixParameterName, path, separator, separatorAtStart, ref param);
        }

        public static Expression<Func<T, bool>> BuildPrefixMatchExpression<T>(string prefixParameterName, string path, char separator, bool separatorAtStart, ref ParameterExpression lambdaParameter)
        {
            if (lambdaParameter == null)
            {
                lambdaParameter = Expression.Parameter(typeof(T), "o");
            }

            string[] segments = path.Split(separator);

            StringBuilder stringBuilder = new StringBuilder();
            Expression queryExpression = null;
            if (separatorAtStart)
            {
                stringBuilder.Append(separator);
                queryExpression = BuildPrefixMatchBranch(lambdaParameter, prefixParameterName, stringBuilder.ToString());
            }

            for (int i = 0; i < segments.Length; i++)
            {
                if (String.IsNullOrEmpty(segments[i].Trim()))
                {
                    continue;
                }

                stringBuilder.Append(segments[i]);

                if (!separatorAtStart)
                {
                    stringBuilder.Append(separator);
                }

                // Create a query expression based on the url prefix
                Expression branch = BuildPrefixMatchBranch(lambdaParameter, prefixParameterName, stringBuilder.ToString());

                // If there is no query expression
                if (queryExpression == null)
                {
                    // Make this branch the current query expression
                    queryExpression = branch;
                }
                else
                {
                    // Append this branch to the current query expression using an OR
                    queryExpression = Expression.OrElse(queryExpression, branch);
                }

                if (separatorAtStart)
                {
                    stringBuilder.Append(separator);
                }
            }
            return Expression.Lambda<Func<T, bool>>(queryExpression, lambdaParameter);
        }

        private static BinaryExpression BuildPrefixMatchBranch(Expression target, string prefixParameterName, string urlPrefix)
        {
            return Expression.Equal(Expression.Property(target, prefixParameterName), Expression.Constant(urlPrefix));
        }

        /// <summary> 解析Expression，取回Member名称及参数类型 </summary>
        public static ExpressionMemberInfo GetMemberName(Expression exp)
        {
            switch (exp.NodeType)
            {
                case ExpressionType.Convert:
                case ExpressionType.TypeAs:
                    return GetMemberName(((UnaryExpression)exp).Operand);
                case ExpressionType.Constant:
                    return GetExpressionMemberInfo(((ConstantExpression)exp).Value.ToString());
                case ExpressionType.MemberAccess:
                    return GetExpressionMemberInfo(((MemberExpression)exp).Member.Name);
                case ExpressionType.Call:
                    return VisitMethod((MethodCallExpression)exp);
                case ExpressionType.Lambda:
                    return GetMemberName(((LambdaExpression)exp).Body);
                case ExpressionType.New:
                    return VisitNew((NewExpression)exp);
                default:
                    throw new NotSupportedException(String.Format("未處理的Expression : '{0}'", exp.NodeType));
            }
        }

        private static ExpressionMemberInfo GetExpressionMemberInfo(string memberName)
        {
            return new ExpressionMemberInfo() { MemberName = memberName };
        }

        /// <summary> 解析MethodCallExpression </summary>
        protected static ExpressionMemberInfo VisitMethod(MethodCallExpression methodExp)
        {
            return new ExpressionMemberInfo()
            {
                MemberName = methodExp.Method.Name,
                ParameterTypes = methodExp.Arguments.Select(p => p.Type)
            };
        }

        /// <summary> 解析NewExpression </summary>
        protected static ExpressionMemberInfo VisitNew(NewExpression newExp)
        {
            return new ExpressionMemberInfo()
            {
                MemberName = ".ctor",
                ParameterTypes = newExp.Arguments.Select(p => p.Type)
            };
        }

        /// <summary> 解析完成後，回傳的類別(包含Member名稱跟Type的陣列) </summary>
        public class ExpressionMemberInfo
        {
            public string MemberName { get; set; }

            public IEnumerable<Type> ParameterTypes { get; set; }
        }

        /// <summary>
        /// Set代理。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="m"></param>
        /// <param name="type"></param>
        /// <returns></returns>
 
        public static Func<T, MethodInfo, object, object> GetSetDelegate<T>(MethodInfo m, Type type)
        {
            var param_obj = Expression.Parameter(typeof(T), "obj");
            var param_val = Expression.Parameter(typeof(object), "val");
            var param_m = Expression.Parameter(typeof(MethodInfo), "m");
            var body_val = Expression.Convert(param_val, type);
            var body = Expression.Call(param_obj, m, body_val);
            Action<T, MethodInfo, object> set = Expression.Lambda<Action<T, MethodInfo, object>>(body, param_obj, param_m, param_val).Compile();
            return (instance, method, v) =>
            {
                set(instance, method, v);
                return null;
            };
        }
       
        /// <summary>
        /// Get代理。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="m"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Func<T, MethodInfo, object> GetGetDelegate<T>(MethodInfo m, Type type)
        {
            var param_obj = Expression.Parameter(typeof(T), "obj");
            var param_m = Expression.Parameter(typeof(MethodInfo), "m");
            MethodCallExpression methodCall = Expression.Call(param_obj, m);
            UnaryExpression castMethodCall = Expression.Convert(
                    methodCall, typeof(object));
            Func<T, MethodInfo, object> get = Expression.Lambda<Func<T, MethodInfo, object>>(castMethodCall, param_obj, param_m).Compile();
            return (instance, method) =>
            {
                return get(instance, method);
            };
        }


        public static string BinarExpressionProvider(Expression left, Expression right, ExpressionType type)
        {
            string sb = "(";
            //先处理左边
            string tmpStr = ExpressionRouter(left);
            if (tmpStr == "null")
            {
                Expression temp = left;
                left = right;
                right = temp;
            }
            sb += ExpressionRouter(left);
            sb += ExpressionTypeCast(type);
            //再处理右边
            tmpStr = ExpressionRouter(right);
            if (tmpStr == "null")
            {
                if (sb.EndsWith(" ="))
                    sb = sb.Substring(0, sb.Length - 2) + " is null";
                else if (sb.EndsWith("<>"))
                    sb = sb.Substring(0, sb.Length - 2) + " is not null";
            }
            else
                sb += tmpStr;
            return sb += ")";
        }
        // 表达式路由。
        public static string ExpressionRouter(Expression exp)
        {
            string sb = string.Empty;
            if (exp is BinaryExpression)
            {
                BinaryExpression be = ((BinaryExpression)exp);
                return BinarExpressionProvider(be.Left, be.Right, be.NodeType);
            }
            else if (exp is MemberExpression)
            {
                if (!exp.ToString().StartsWith("value("))
                {
                    MemberExpression me = ((MemberExpression)exp);
                    return me.Member.Name;
                }
                else
                {
                    var result = Expression.Lambda(exp).Compile().DynamicInvoke();
                    if (result == null)
                        return "null";
                    if (result is ValueType)
                        return result.ToString();
                    else if (result is string || result is DateTime || result is char)
                        return string.Format("'{0}'", (result as string).Replace("'", "''"));
                }
            }
            else if (exp is NewArrayExpression)
            {
                NewArrayExpression ae = ((NewArrayExpression)exp);
                StringBuilder tmpstr = new StringBuilder();
                foreach (Expression ex in ae.Expressions)
                {
                    tmpstr.Append(ExpressionRouter(ex));
                    tmpstr.Append(",");
                }
                return tmpstr.ToString(0, tmpstr.Length - 1);
            }
            else if (exp is MethodCallExpression)
            {
                MethodCallExpression mce = (MethodCallExpression)exp;
                if (mce.Method.Name == "Like")
                    return string.Format("({0} like {1})", ExpressionRouter(mce.Arguments[0]), ExpressionRouter(mce.Arguments[1]));
                else if (mce.Method.Name == "NotLike")
                    return string.Format("({0} Not like {1})", ExpressionRouter(mce.Arguments[0]), ExpressionRouter(mce.Arguments[1]));
                else if (mce.Method.Name == "In")
                    return string.Format("{0} In ({1})", ExpressionRouter(mce.Arguments[0]), ExpressionRouter(mce.Arguments[1]));
                else if (mce.Method.Name == "NotIn")
                    return string.Format("{0} Not In ({1})", ExpressionRouter(mce.Arguments[0]), ExpressionRouter(mce.Arguments[1]));
            }
            else if (exp is ConstantExpression)
            {
                ConstantExpression ce = ((ConstantExpression)exp);
                if (ce.Value == null)
                    return "null";
                else if (ce.Value is ValueType)
                    return ce.Value.ToString();
                else if (ce.Value is string || ce.Value is DateTime || ce.Value is char)
                    return string.Format("'{0}'", (ce.Value as string).Replace("'", "''"));
            }
            else if (exp is UnaryExpression)
            {
                UnaryExpression ue = ((UnaryExpression)exp);
                return ExpressionRouter(ue.Operand);
            }
            return null;
        }
        // 表达式类型转换。
        public static string ExpressionTypeCast(ExpressionType type)
        {
            switch (type)
            {
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    return " AND ";
                case ExpressionType.Equal:
                    return " =";
                case ExpressionType.GreaterThan:
                    return " >";
                case ExpressionType.GreaterThanOrEqual:
                    return ">=";
                case ExpressionType.LessThan:
                    return "<";
                case ExpressionType.LessThanOrEqual:
                    return "<=";
                case ExpressionType.NotEqual:
                    return "<>";
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    return " Or ";
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                    return "+";
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return "-";
                case ExpressionType.Divide:
                    return "/";
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    return "*";
                default:
                    return null;
            }

        }
    }
}
